#pragma once
#include <iostream>
#include <string>
#include <ctime>
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <cstdio>
#include <pthread.h>
#include "lock_guard.hpp"
// 面向过程实现简单日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
bool g_issave_file = false;            // 是否将日志输出到文件的标识符
const std::string LogName = "log.txt"; // 日志输出的目标文件
// 日志等级
enum Log
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

// 将日志等级转化为字符串
std::string LevelToString(int level)
{
    std::string str;
    switch (level)
    {
    case DEBUG:
        str = "DEBUG";
        break;
    case INFO:
        str = "INFO";
        break;
    case WARNING:
        str = "WARNING";
        break;
    case ERROR:
        str = "ERROR";
        break;
    case FATAL:
        str = "FATAL";
        break;
    default:
        return "UNKNOW";
    }
    return str;
}

// 获取当前时间的字符串
std::string GetTimeStr()
{
    time_t currtime = time(nullptr);               // 获取当前时间戳
    struct tm *format_time = localtime(&currtime); // 将当前时间戳转化为当地时间表示的结构体
    if (format_time == nullptr)
        return "NONE";
    char timestr[64];
    snprintf(timestr, sizeof(timestr), "%d-%d-%d %d:%d:%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return timestr;
}
void EnableSaveFile(std::string message)
{
    std::ofstream out(LogName, std::ios::app); // 以追加模式打开
    if (!out.is_open())                        // 打开失败
    {
        return;
    }
    out << message; // 将message写入
    out.close();
}
// 提供给外界的接口，文件名，行数，是否输出到文件中，日志等级，要输出的消息（采用可变参数，类似printf的格式输出字符串）
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{
    std::string LevelStr = LevelToString(level);
    std::string TimeStr = GetTimeStr();
    pid_t selfpid = getpid();
    char buffer[1024];
    va_list arg;                                    // 处理可变参数的数据类型
    va_start(arg, format);                          // 用可变参数列表format初始化arg
    vsnprintf(buffer, sizeof(buffer), format, arg); // 对valist的类型进行格式化输出
    va_end(arg);                                    // 结束可变参数的处理

    std::string message =
        "[" + TimeStr + "]" +
        "[" + LevelStr + "]" +
        "[" + filename + "]" +
        "[" + std::to_string(line) + "]" +
        "[" + std::to_string(selfpid) + "]" +
         + buffer + "\n";

    Lockguard lockgu(lock);

    if (!g_issave_file) // 输出到显示器
    {
        std::cout << message << std::endl;
    }
    else // 输出到文件
    {
        EnableSaveFile(message);
    }
}

// c99新特性，__VA_ARGS__,表示可变参数的参数列表，加##即可以为空
#define Log(level, format, ...)                                                      \
    do                                                                               \
    {                                                                                \
        LogMessage(__FILE__, __LINE__, g_issave_file, level, format, ##__VA_ARGS__); \
    } while (0) // 只执行一次

// 更改日志输出文件
#define EnableScreen()         \
    do                         \
    {                          \
        g_issave_file = false; \
    } while (0)

#define EnableFile()          \
    do                        \
    {                         \
        g_issave_file = true; \
    } while (0)